package com.foxchen.offline.task.autoconfigure;


import com.foxchen.offline.task.TaskHandlerFactory;
import com.foxchen.offline.task.config.OfflineTaskConfig;
import com.foxchen.offline.task.controller.OfflineTaskController;
import com.foxchen.offline.task.runner.ExportApplicationRunner;
import com.foxchen.offline.task.service.OfflineTaskService;
import com.foxchen.offline.task.service.impl.OfflineTaskServiceImpl;
import com.github.pagehelper.PageInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 通用
 */
@Slf4j
@Configuration
@EnableConfigurationProperties({OfflineTaskConfig.class})
@ConditionalOnProperty(prefix = "foxchen.offline.task.enabled",value = "true",matchIfMissing = true)
@ConditionalOnBean({DataSource.class})
@AutoConfigureAfter({RedissonAutoConfiguration.class})
public class OfflineTaskHandlerFactoryAutoConfiguration {
    @Resource
    private OfflineTaskConfig offlineTaskConfig;

    private static final String THREAD_POOL_NAME = "offline-task-pool";

    /**
     * 异步任务的线程池
     * @return 异步任务线程池
     */
    @Bean(name = "offlineTaskThreadPoolExecutor")
    public ThreadPoolExecutor threadPoolExecutor(){
        // 创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                offlineTaskConfig.getCorePoolSize(),
                offlineTaskConfig.getMaximumPooSize(),
                offlineTaskConfig.getKeepAliveTime(),
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(offlineTaskConfig.getQueueCapacity()),
                new CustomRejectionHandler());

        // 线程名字
        threadPoolExecutor.setThreadFactory(runnable -> {
            Thread thread = new Thread(runnable);
            thread.setName(THREAD_POOL_NAME + StringUtils.SPACE + thread.getId());
            return thread;
        });

        return threadPoolExecutor;
    }

    /**
     * 异步任务的工厂类
     */
    @Bean
    @DependsOn("offlineTaskThreadPoolExecutor")
    public TaskHandlerFactory exportHandlerFactory() {
		return new TaskHandlerFactory();
	}

    /**
     * 异步任务的初始化类
     */
    @Bean
    public ExportApplicationRunner exportApplicationRunner(){
        return new ExportApplicationRunner();
    }


    /**
     * 异步任务的操作业务类
     */
    @Bean
    public OfflineTaskService offlineTaskService(){
        return new OfflineTaskServiceImpl();
    }

    /**
     * 异步任务的操作业务类
     */
    @Bean
    public OfflineTaskController offlineTaskController(){
        return new OfflineTaskController();
    }

    @Bean
    public PageInterceptor pageHelper() {
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.setProperty("helperDialect", "mysql"); // 使用的数据库方言
        properties.setProperty("reasonable", "true");
        pageInterceptor.setProperties(properties);
        return pageInterceptor;
    }

    /**
     * 异步任务的线程池队列拒绝策略
     */
    private static class CustomRejectionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            throw new ThreadPoolMaxExecutingException("Task " + r.toString() + " rejected from " + executor.toString() + ",Reason: queue max.");
        }

        // 自定义异常类
        private static class ThreadPoolMaxExecutingException extends RuntimeException {
            public ThreadPoolMaxExecutingException(String message) {
                super(message);
            }
        }
    }

}
